package com.dlkw.easyroom;

import android.content.Context;

import androidx.room.Room;
import androidx.room.RoomDatabase;

import com.dlkw.easyroom.common.Consts;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.WeakHashMap;

@SuppressWarnings("all")
public class EasyRoom {
    private static volatile RoomDatabase INSTANCE;

    private static final Map<Class<? extends BaseDao<?>>, BaseDao> daoMap = new WeakHashMap<>();

    public static synchronized void init(Context context, String name) {
        init(context, name, null, null);
    }

    public static synchronized void init(Context context, String name, InitInterceptor interceptor) {
        init(context, name, interceptor, null);
    }

    public static synchronized void init(Context context, String name, RoomDatabase.Callback callback) {
        init(context, name, null, callback);
    }

    public static synchronized void init(Context context, String name, InitInterceptor interceptor, RoomDatabase.Callback callback) {
        try {
            Class<?> cls = Class.forName(Consts.EASY_ROOM_PACKAGE + "." + Consts.DB_CLASS_KEEPER);
            Field field = cls.getDeclaredField("DB_CLASS");
            Class<? extends RoomDatabase> dbClass = (Class<? extends RoomDatabase>) field.get(null);
            RoomDatabase.Builder<? extends RoomDatabase> builder = Room.databaseBuilder(context, dbClass, name);
            if (null == interceptor) {
                builder.allowMainThreadQueries();
            } else {
                interceptor.intercept(builder);
            }
            if (null != callback) {
                builder.addCallback(callback);
            }
            init(builder.build());
        } catch (Exception e) {
            throw new RuntimeException("EasyRoom初始化出错", e);
        }
    }

    public static synchronized void init(RoomDatabase database) {
        INSTANCE = database;
    }

    public static <D, T extends BaseDao<D>> T getDao(Class<T> clazz) {
        BaseDao dao = daoMap.get(clazz);
        if (null != dao) {
            return (T) dao;
        }
        if (null == INSTANCE) {
            throw new NullPointerException("Please call EasyRoom.init() first");
        }
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException("The class must be an interface");
        }
        RoomDatabase db = INSTANCE;
        Method[] methods = db.getClass().getMethods();
        for (Method method : methods) {
            if (method.getReturnType() == clazz && method.getParameterTypes().length == 0) {
                try {
                    method.setAccessible(true);
                    dao = createDao(clazz, (T) method.invoke(db));
                    daoMap.put(clazz, dao);
                    break;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return (T) dao;
    }

    private static <T> T createDao(Class<T> clazz, Object finalDao) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getDeclaringClass() == Object.class) {
                    return method.invoke(this, args);
                }
                method.setAccessible(true);
                return method.invoke(finalDao, args);
            }
        });
    }

    public static <D extends BaseDao> String getTableName(D baseDao) {
        return getTableName(baseDao.getClass());
    }

    public static String getTableName(Class<? extends BaseDao> dao) {
        try {
            String daoClassName = dao.getSimpleName();
            if (daoClassName.endsWith("Dao_Impl")) {
                daoClassName = daoClassName.substring(0, daoClassName.lastIndexOf("_"));
            }
            Class<?> cls = Class.forName(Consts.EASY_ROOM_PACKAGE + ".$$" + daoClassName + "Keeper");
            Field field = cls.getDeclaredField("ENTITY_CLASS");
            Class<?> entityClass = (Class<?>) field.get(null);
            cls = Class.forName(Consts.EASY_ROOM_PACKAGE + ".$$" + entityClass.getSimpleName() + "Keeper");
            field = cls.getDeclaredField("TABLE_NAME");
            return (String) field.get(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String getViewName(Object view) {
        return getViewName(view.getClass());
    }

    public static String getViewName(Class<?> view) {
        try {
            String viewClassName = view.getSimpleName();
            if (viewClassName.endsWith("_Impl")) {
                viewClassName = viewClassName.substring(0, viewClassName.lastIndexOf("_"));
            }
            Class<?> cls = Class.forName(Consts.EASY_ROOM_PACKAGE + ".$$" + viewClassName + "Keeper");
            Field field = cls.getDeclaredField("VIEW_NAME");
            return (String) field.get(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
